package com.qkun.library.utils;

import android.os.Handler;
import android.os.Looper;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;

public final class ThreadUtils {

    public static final Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());

    private ThreadUtils() {
    }

    public static void runInUiThread(Runnable task, long delayMillis) {
        MAIN_HANDLER.postDelayed(task, delayMillis);
    }

    public static boolean isInMainThread() {
        return Thread.currentThread() == MAIN_HANDLER.getLooper().getThread();
    }

    public static void runInUiThread(Runnable task) {
        runInUiThread(task, 0);
    }

    /**
     * 创建一个定长线程池，可控制线程最大并发数，超出的线程会在队列中等待
     *
     * @param nThreads
     * @return
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return Executors.newFixedThreadPool(nThreads);
    }

    /**
     * 创建一个单线程化的线程池，它只会用唯一的工作线程来执行任务，保证所有任务按照指定顺序执行
     *
     * @return
     */
    public static ExecutorService newSingleThreadExecutor() {
        return Executors.newSingleThreadExecutor();
    }

    /**
     * 创建一个可缓存线程池，线程池的最大长度无限制，但如果线程池长度超过处理需要，可灵活回收空闲线程，若无可回收，则新建线程
     *
     * @return
     */
    public static ExecutorService newCachedThreadPool() {
        return Executors.newCachedThreadPool();
    }

    /**
     * 创建一个单线程执行器，该执行器可以安排命令在给定的延迟后运行或定期执行
     * （但是请注意，如果该单个线程由于在关闭之前执行期间由于执行失败而终止，则在需要执行新任务时将使用新线程代替）
     * 保证任务按顺序执行，并且活动的任务不超过一个 在任何给定时间
     * 与其他等效的{@code Executors.newScheduledThreadPool(1)}不同，保证返回的执行程序不可重新配置为使用其他线程
     *
     * @return
     */
    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return Executors.newSingleThreadScheduledExecutor();
    }

    /**
     * 创建一个定长线程池，支持定时及周期性任务执行
     *
     * @param corePoolSize 即使在空闲状态下要保留在池中的线程数
     * @return
     */
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return Executors.newScheduledThreadPool(corePoolSize);
    }

    /**
     * 平行去做
     *
     * @param calls
     * @param nThreads
     * @param <T>      继承于{@code Callable}的类的实例
     * @param <V>      方法{@code call()}的结果类型
     * @return
     * @throws Exception
     */
    public <T extends Callable<V>, V> List<V> parallelToDo(List<T> calls, int nThreads) throws Exception {
        if (!Utils.isEmpty(calls)) {
            final int N = calls.size();
            nThreads = Math.min(nThreads, N);
            ExecutorService service = (nThreads < 2) ? newSingleThreadExecutor() : newFixedThreadPool(nThreads);
            List<Future<V>> futures = new ArrayList<>();
            for (int i = 0; i < N; i++) {
                futures.add(service.submit(Utils.requireNonNull(calls.get(i))));
            }
            // 启动有序关闭，在该关闭中执行先前提交的任务，但不接受任何新任务。
            // 如果已关闭，则调用不会产生任何其他影响。此方法不等待先前提交的任务完成执行
            service.shutdown();

            List<V> outputs = new ArrayList<>();
            for (int i = 0; i < N; i++) {
                outputs.add(futures.get(i).get());
            }
            return outputs;
        }
        return null;
    }

    public <T extends Callable<V>, V> List<V> parallelToDo(List<T> calls) throws Exception {
        //默认的话让nThreads等于虚拟机可用的最大处理器数
        return parallelToDo(calls, Runtime.getRuntime().availableProcessors());
    }

}
